﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Net;
using Library.HttpTools;
using Library.Logging;

namespace iNotesNotification
{
    public class iNotesController
    {
        protected static iNotesController _instance;
        protected static ILog _log;
        protected static FileInfo _assemblyLocation;

        protected iNotesHelper _notesHelper;
        protected MiniBrowser _browser;
        protected iNotesMailbox _mailbox = null;
        protected FileInfo _lastTempFile = null;
        protected FileInfo _currentSavedFile = null;
        protected FileInfo _prevSavedFile = null;
        protected bool _hasLoggedIn = false;
        protected bool? _hasNewMessages = null;
        protected string _lastLoadedInterface = string.Empty;
        protected ProcessController _controller = new ProcessController();

        public static iNotesController Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new iNotesController();

                }
                return _instance;
            }
        }

        public static ILog Log
        {
            get
            {
                if (_log == null)
                {
                    _log = new GarbageLog();

                }
                return _log;
            }
        }

        public static FileInfo AssemblyLocation
        {
            get
            {
                if (_assemblyLocation == null)
                {
                    _assemblyLocation = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);
                }
                return _assemblyLocation;
            }
        }

        private static string NotesXmlDateFormat(DateTime dt)
        {
            return dt.ToUniversalTime().ToString("yyyyMMddTHHmmss") + ",00Z"; //20020101T140000,00Z
        }

        public static string ReplaceUriTags(string uri, string folder)
        {
            iNotesConfig conf = iNotesConfig.Instance;

            string 
                start = "1",
                count = conf.CountToRetrieve.ToString(),
                username = conf.Login.Replace('.', '_'),
                startTime = NotesXmlDateFormat(DateTime.Now.AddDays(-1 * Math.Abs(conf.DaysToRetrieve))),
                untilTime = NotesXmlDateFormat(DateTime.Now.AddDays(1));

           uri = uri
                .Replace("{USERID}", username)
                .Replace("{FOLDER}", folder ?? "Inbox")
                .Replace("{START}", start.ToString())
                .Replace("{COUNT}", count.ToString())
                .Replace("{START_TIME}", startTime)
                .Replace("{END_TIME}", untilTime);

            return uri;
        }

        protected iNotesController()
        {
        }

        public iNotesHelper Helper
        {
            get
            {
                string iface = iNotesConfig.Instance.Interface;

                if ((_lastLoadedInterface != iface) || (_notesHelper == null))
                {
                    _lastLoadedInterface = iface;
                    if (_mailbox != null) _mailbox.Clear();
                    RefreshFileNames();

                    switch (iNotesConfig.Instance.Interface)
                    {
                        case "xml":
                            _notesHelper = new iNotesXmlHelper();
                            break;
                        case "html":
                            _notesHelper = new iNotesHtmlHelper();
                            break;
                        default:
                            // Need to replace this with a reflection based plugin, or dynamically compiled plugin.
                            // Would scripting would be better for quick fixes?
                            _notesHelper = new iNotesHtmlHelper();
                            break;
                    }
                }

                return _notesHelper;
            }
        }

        public MiniBrowser Browser
        {
            get 
            {
                if (_browser == null)
                {
                    _browser = new MiniBrowser(iNotesController.Log);
                    _browser.Timeout = 30;
                }
                else
                {
                    if (_browser.Log != iNotesController.Log) _browser.Log = iNotesController.Log;
                }
                return _browser;
            }
        }

        public bool IsLoginValid
        {
            get { return login(true); }
        }

        public bool Download(FileInfo fi)
        {
            return login(false, fi);
        }

        public void StartAsyncRefresh()
        {
            _lastTempFile = new FileInfo(Path.GetTempFileName());
            _controller.Download(_lastTempFile);
        }

        public bool IsAsyncRefreshCompleted
        {
            get { return !_controller.IsRunning; }
        }

        public bool AsyncRefreshResult
        {
            get { return _controller.WasSuccessfull; }
        }

        public void RefreshAfterAsync()
        {
            RotateMailFiles();
            if (SavedFile.Exists && this.parseFile(SavedFile))
            {
                _mailbox.Sort();
            }
        }

        public void Refresh()
        {
            iNotesConfig conf = iNotesConfig.Instance;

            Helper.Refresh();
            _hasNewMessages = null;

            if (!_hasLoggedIn)
            {
                if (login())
                {
                    if ((_lastTempFile != null) && _lastTempFile.Exists)
                    {
                        _mailbox.Sort();
                    }
                }
            }
            else
            {
                if (_lastTempFile == null) _lastTempFile = new FileInfo(Path.GetTempFileName());
                else if (_lastTempFile.Exists)
                {
                    try
                    {
                        _lastTempFile.Delete();
                        _lastTempFile.Refresh();
                    }
                    catch { _lastTempFile = new FileInfo(Path.GetTempFileName()); }
                }

                if (_lastTempFile != null)
                {
                    using (FileStream fs = _lastTempFile.Create())
                    {
                        NetworkCredential credentials = new NetworkCredential(conf.Login, conf.Password);
                        this.Browser.Credentials = credentials;

                        this.Browser.RedirectOutputStream = fs;
                        this.Browser.HttpGet(Helper.MailUri);
                        fs.Close();
                        this.Browser.RedirectOutputStream = null;
                    }
                    _lastTempFile.Refresh();

                    if (parseFile(_lastTempFile))
                    {
                        RotateMailFiles();
                        _mailbox.Sort();
                    }
                    else
                    {
                        _lastTempFile.Delete();

                        if (login())
                        {
                            if ((_lastTempFile != null) && _lastTempFile.Exists)
                            {
                                _mailbox.MarkAsRead();
                                _mailbox.Sort();
                            }
                        }
                    }
                }
            }
        }

        public DateTime LatestMessageDate
        {
            get { return Helper.LatestMessageDate; }
        }

        public int NumberMessages
        {
            get { return Helper.NumberMessages; }
        }

        public string LoginUrl
        {
            get { return "/names.nsf?Login"; }
        }

        public FileInfo SavedFile
        {
            get 
            {
                if (_currentSavedFile == null)
                {
                    _currentSavedFile = new FileInfo(AssemblyLocation.Directory.FullName + "\\" + Helper.MailLocalFileName);
                }
                return _currentSavedFile; 
            }
        }

        public FileInfo PreviousSavedFile
        {
            get
            {
                if (_prevSavedFile == null)
                {
                    _prevSavedFile = new FileInfo(AssemblyLocation.Directory.FullName + "\\" + Helper.MailLocalFileName + ".previous");
                } 
                return _prevSavedFile; 
            }
        }

        public iNotesMailbox Mailbox
        {
            get
            {
                if (_mailbox == null)
                {
                    _mailbox = new iNotesMailbox();
                    if (SavedFile.Exists)
                    {
                        if (parseFile(this.SavedFile))
                        {
                            _mailbox.Sort();
                            _mailbox.MarkAsRead();
                        }
                        else
                        {
                            this.SavedFile.Delete();
                        }
                    }
                }
                return _mailbox;
            }
        }

        protected void RotateMailFiles()
        {
            if (SavedFile.Exists)
            {
                if (PreviousSavedFile.Exists) PreviousSavedFile.Delete();
                SavedFile.CopyTo(this.PreviousSavedFile.FullName);
                PreviousSavedFile.Refresh();
            }

            if ((_lastTempFile != null) && _lastTempFile.Exists)
            {
                if (SavedFile.Exists) SavedFile.Delete();
                _lastTempFile.CopyTo(this.SavedFile.FullName);
                SavedFile.Refresh();
            }

            if (_lastTempFile != null) _lastTempFile.Delete();
        }

        protected virtual bool login()
        {
            return login(false, null);
        }

        protected virtual bool login(bool isTest)
        {
            return login(isTest, null);
        }

        protected virtual bool login(bool isTest, FileInfo file)
        {
            bool loginSuccessful = false;
            bool basicAuthWorked = false;
            try
            {
                iNotesConfig conf = iNotesConfig.Instance;

                NetworkCredential credentials = new NetworkCredential(conf.Login, conf.Password);
                this.Browser.Credentials = credentials;

                string url = this.Helper.MailUri;

                _lastTempFile = new FileInfo(Path.GetTempFileName());
                FileStream fs = _lastTempFile.Create();

                this.Browser.RedirectOutputStream = fs;
                bool httpSuccess = this.Browser.HttpGet(url);
                fs.Close();
                this.Browser.RedirectOutputStream = null;

                if (httpSuccess)
                {
                    _lastTempFile.Refresh();
                    if (test(_lastTempFile))
                    {
                        basicAuthWorked = true;
                    }
                    else
                    {
                        fs = _lastTempFile.Create();
                        this.Browser.RedirectOutputStream = fs;

                        this.Browser.Input["redirectto"] = url;
                        this.Browser.Input["username"] = conf.Login;
                        this.Browser.Input["password"] = conf.Password;

                        httpSuccess = this.Browser.HttpPost(LoginUrl);
                        fs.Close();
                        this.Browser.RedirectOutputStream = null;

                        _lastTempFile.Refresh();
                    }

                    if (isTest)
                    {
                        if (basicAuthWorked || test(_lastTempFile))
                        {
                            loginSuccessful = true;
                            _lastTempFile.Delete();
                        }
                    }
                    else if (file != null)
                    {
                        if (basicAuthWorked || test(_lastTempFile))
                        {
                            loginSuccessful = true;
                            if (file.Exists) file.Delete();
                            _lastTempFile.MoveTo(file.FullName);
                        }
                    }
                    else
                    {
                        bool parseWorked = basicAuthWorked ? parseFile(_lastTempFile) : parseAndTestFile(_lastTempFile);

                        if (parseWorked)
                        {
                            loginSuccessful = true;
                            RotateMailFiles();
                        }
                        else
                        {
                            _lastTempFile.Delete();
                        }
                    }
                }
            }
            catch (Exception)
            {
                loginSuccessful = false;
            }

            return loginSuccessful;
        }

        protected virtual bool parseFile(FileInfo file)
        {
            bool passed = false;

            try
            {
                Helper.Parse(file, Mailbox);
                passed = true;
            }
            catch (Exception ex)
            {
                Program.LogException(ex);
            }

            return passed;
        }

        protected virtual bool parseAndTestFile(FileInfo file)
        {
            bool passed = test(file);
            if (passed)
            {
                try
                {
                    Helper.Parse(file, Mailbox);
                    passed = true;
                }
                catch (Exception)
                {
                    passed = false;
                }
            }

            return passed;
        }

        protected virtual bool test(FileInfo file)
        {
            bool passed = true;
            try
            {
                using (StreamReader reader = new StreamReader(file.OpenRead()))
                {
                    string line = reader.ReadLine();
                    while (line != null)
                    {
                        if (line.Contains("Server Login") || line.Contains("/names.nsf?Login"))
                        {
                            passed = false;
                            break;
                        }
                        line = reader.ReadLine();
                    }
                }
            }
            catch { }

            return passed;
        }

        private void RefreshFileNames()
        {
            _lastTempFile = null;
            _currentSavedFile = null;
            _prevSavedFile = null;
        }     

        public string InboxUrl
        {
            get
            {
                iNotesConfig conf = iNotesConfig.Instance;
                int start = 1, count = conf.CountToRetrieve;

                StringBuilder sb = new StringBuilder();
                sb.Append(conf.NotesFileUrl.Replace("{USERID}", conf.Login.Replace('.', '_')))
                  .Append(conf.HtmlUrlSuffix.Replace("{FOLDER}", "Inbox").Replace("{START}", start.ToString()).Replace("{COUNT}", count.ToString()));

                return sb.ToString();
            }
        }

        public static void LaunchBrowser_Inbox()
        {
            iNotesConfig conf = iNotesConfig.Instance;
            bool isBrowserOverride = conf.IsBrowserOverridden;
            string browserpath = conf.BrowserPath;
            string url = conf.EmailListUrl("Inbox");

            if (isBrowserOverride ||
                conf.Browser.StartsWith("f", StringComparison.CurrentCultureIgnoreCase) ||
                conf.Browser.StartsWith("c", StringComparison.CurrentCultureIgnoreCase))
            {
                System.Diagnostics.Process.Start(browserpath, url);
            }
            else
            {
                using (IEAutomation.IEDriver driver = new IEAutomation.IEDriver(url))
                {
                    if (driver.LoadedSuccessfully)
                    {
                        try
                        {
                            NetworkCredential credentials = new NetworkCredential(iNotesConfig.Instance.Login, iNotesConfig.Instance.Password);
                            driver.Navigate(url, credentials);
                            MSHTML.IHTMLInputElement usr = driver.GetElementById("username", 10) as MSHTML.IHTMLInputElement;
                            MSHTML.IHTMLInputElement pwd = driver.GetElementById("password", 10) as MSHTML.IHTMLInputElement;

                            if (usr != null && pwd != null)
                            {
                                usr.value = iNotesConfig.Instance.Login;
                                pwd.value = iNotesConfig.Instance.Password;

                                driver.SubmitForm();
                            }
                        }
                        catch { }
                    }
                    driver.Dispose();
                }
            }
        }

        public static void LaunchBrowser_Email(string id)
        {
            iNotesConfig conf = iNotesConfig.Instance;
            bool isBrowserOverride = conf.IsBrowserOverridden;
            string browserpath = conf.BrowserPath;
            string url = conf.EmailDetailUrl("Inbox", id);

            if (isBrowserOverride ||
                conf.Browser.StartsWith("f", StringComparison.CurrentCultureIgnoreCase) ||
                conf.Browser.StartsWith("c", StringComparison.CurrentCultureIgnoreCase))
            {
                System.Diagnostics.Process.Start(browserpath, url);
            }
            else
            {
                using (IEAutomation.IEDriver driver = new IEAutomation.IEDriver(url))
                {
                    if (driver.LoadedSuccessfully)
                    {
                        try
                        {
                            NetworkCredential credentials = new NetworkCredential(iNotesConfig.Instance.Login, iNotesConfig.Instance.Password);
                            driver.Navigate(url, credentials);

                            MSHTML.IHTMLInputElement usr = driver.GetElementById("username", 10) as MSHTML.IHTMLInputElement;
                            MSHTML.IHTMLInputElement pwd = driver.GetElementById("password", 10) as MSHTML.IHTMLInputElement;

                            if (usr != null && pwd != null)
                            {
                                usr.value = iNotesConfig.Instance.Login;
                                pwd.value = iNotesConfig.Instance.Password;

                                driver.SubmitForm();
                            }
                        }
                        catch { }
                    }
                    driver.Dispose();
                }
            }
        }  
    }
}
